#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <pwd.h>
#include <limits.h>
#include <assert.h>

#include "web_cmd.h"
#include "web_log.h"

// 函数声明
static int validate_username_password(const char *username, const char *passwd);

// 命令缓冲区大小定义
#define CMD_BUF_SIZE (PATH_MAX * 2)

// 全局变量，存储展开后的htpasswd文件路径
static char g_htpasswd_path[256] = {0};

// 展开路径中的波浪号（~）
static int expand_tilde(const char *path, char *expanded_path, size_t size)
{
    if (path == NULL || expanded_path == NULL || size == 0) {
        return -1;
    }

    // 如果路径不以波浪号开头，直接复制
    if (path[0] != '~') {
        strncpy(expanded_path, path, size - 1);
        expanded_path[size - 1] = '\0';
        return 0;
    }

    struct passwd *pw;
    const char *username = NULL;
    size_t username_len = 0;

    // 检查是否是 "~/" 或者 "~"
    if (path[1] == '/' || path[1] == '\0') {
        // 当前用户的主目录
        pw = getpwuid(getuid());
        if (pw == NULL) {
            return -1;
        }
        
        // 跳过波浪号和可能的斜杠
        path = (path[1] == '/') ? path + 2 : path + 1;
    } else {
        // 指定用户的主目录，如 "~username/"
        const char *slash = strchr(path + 1, '/');
        if (slash) {
            username_len = slash - (path + 1);
            username = path + 1;
            path = slash + 1;
        } else {
            username = path + 1;
            path = "";
        }

        // 获取指定用户的密码条目
        char username_buf[128] = {0};
        strncpy(username_buf, username, username_len);
        username_buf[username_len] = '\0';
        
        pw = getpwnam(username_buf);
        if (pw == NULL) {
            return -1;
        }
    }

    // 构建展开后的路径
    snprintf(expanded_path, size, "%s/%s", pw->pw_dir, path);
    return 0;
}

// 获取展开后的htpasswd文件路径
const char* web_cmd_get_htpasswd_path(void)
{
    return g_htpasswd_path;
}

int web_cmd_init(void)
{
    // 展开波浪号路径
    if (expand_tilde(WEB_HTPASSSWD_FILE_DEFAULT, g_htpasswd_path, sizeof(g_htpasswd_path)) != 0) {
        log_error("Failed to expand path: %s", WEB_HTPASSSWD_FILE_DEFAULT);
        return -1;
    }

    log_info("Expanded htpasswd path: %s", g_htpasswd_path);

    // 检查文件是否存在
    if (0 != access(g_htpasswd_path, F_OK)) {
        FILE * fp = NULL;
        char cmd_str[CMD_BUF_SIZE];
        char dir_path[256];
        char *last_slash;
        
        // 复制文件路径
        strncpy(dir_path, g_htpasswd_path, sizeof(dir_path) - 1);
        dir_path[sizeof(dir_path) - 1] = '\0';
        
        assert(strlen(dir_path) < (CMD_BUF_SIZE - 10)); // "mkdir -p " 占9字节 + 空字符
        
        // 查找最后一个斜杠，以获取目录路径
        last_slash = strrchr(dir_path, '/');
        if (last_slash != NULL) {
            // 将最后一个斜杠后的内容截断，只保留目录路径
            *last_slash = '\0';
            
            // 检查目录是否存在，如果不存在则创建
            if (0 != access(dir_path, F_OK)) {
                log_info("Directory %s does not exist, creating it...", dir_path);
                snprintf(cmd_str, sizeof(cmd_str), "mkdir -p %s", dir_path);
                fp = popen(cmd_str, "r");
                if (NULL != fp) {
                    pclose(fp);
                }
            }
        }
        
        // 创建密码文件
        assert(strlen(g_htpasswd_path) < (CMD_BUF_SIZE - 7)); // "touch " 占6字节 + 空字符
        snprintf(cmd_str, sizeof(cmd_str), "touch %s", g_htpasswd_path);
        fp = popen(cmd_str, "r");
        if (NULL != fp) {
            pclose(fp);
        }
    }
    
    return 0;
}

int web_cmd_add_user(const char *username, const char *passwd)
{
    FILE * fp;
    char buffer[200];
    char cmd_str[CMD_BUF_SIZE];
    int rt = -1;
    
    // 验证用户名和密码
    if (validate_username_password(username, passwd) != 0) {
        log_error("Invalid username or password for adding user");
        return -1;
    }
    
    // 检查命令长度是否安全
    assert(strlen(g_htpasswd_path) + strlen(username) + strlen(passwd) < 
          (CMD_BUF_SIZE - 30)); // 命令模板占约30字节
    
    // 使用cd /tmp确保在一个有效的工作目录中执行命令
    snprintf(cmd_str, sizeof(cmd_str), "cd /tmp && %s -b %s %s %s 2>&1",
            WEB_CMD_EXEC_HTPASSWD, g_htpasswd_path, username, passwd);
    
    // 打印完整命令（不包含密码）
    log_info("Executing htpasswd command: cd /tmp && %s -b %s %s [PASSWORD] 2>&1",
            WEB_CMD_EXEC_HTPASSWD, g_htpasswd_path, username);
    
    fp = popen(cmd_str, "r");
    if (NULL != fp) {
        memset(buffer, 0, sizeof(buffer));
        fgets(buffer, sizeof(buffer)-1, fp);
        
        if (strstr(buffer, "Adding password") || strstr(buffer, "Updating password")) {
            rt = 0;
        }
        
        log_info("htpasswd Add User result : (%d)%s", rt, buffer);
        pclose(fp);
    }

    return rt;
}

// 验证用户名和密码是否包含非法字符
static int validate_username_password(const char *username, const char *passwd)
{
    // 打印用户名，包括十六进制表示
    log_info("Validating username: '%s',passwd: '%s'", username, passwd);
    
    // 检查用户名是否为空
    if (username == NULL || username[0] == '\0') {
        log_error("Username is empty");
        return -1;
    }
    
    // 检查密码是否为空
    if (passwd == NULL || passwd[0] == '\0') {
        log_error("Password is empty");
        return -1;
    }
    
    // 检查用户名中是否包含非法字符（冒号、空格、引号等）
    const char *invalid_chars = ":'\"\\ \t\n\r";
    for (int i = 0; i < strlen(username); i++) {
        if (strchr(invalid_chars, username[i]) != NULL) {
            log_error("Username contains illegal character: '%c'", username[i]);
            return -1;
        }
    }
    
    // 检查密码中是否包含非法字符（主要是引号，可能导致命令注入）
    const char *invalid_passwd_chars = "'\"\\";
    for (int i = 0; i < strlen(passwd); i++) {
        if (strchr(invalid_passwd_chars, passwd[i]) != NULL) {
            log_error("Password contains illegal character");
            return -1;
        }
    }
    
    return 0;
}

int web_cmd_verity_user(const char *username, const char *passwd) 
{
    FILE * fp = NULL;
    char buffer[200];
    char cmd_str[CMD_BUF_SIZE];
    int rt = VERITY_USER_RT_OTHER;
    
    // 验证用户名和密码
    if (validate_username_password(username, passwd) != 0) {
        log_error("Invalid username or password");
        return VERITY_USER_RT_FAIL;
    }

    // 检查命令长度是否安全
    assert(strlen(g_htpasswd_path) + strlen(username) + strlen(passwd) < 
          (CMD_BUF_SIZE - 30)); // 命令模板占约30字节
    
    // 使用cd /tmp确保在一个有效的工作目录中执行命令
    snprintf(cmd_str, sizeof(cmd_str), "cd /tmp && %s -vb %s %s %s 2>&1",
            WEB_CMD_EXEC_HTPASSWD, g_htpasswd_path, username, passwd);
    
    // 打印完整命令（不包含密码）
    log_info("Executing htpasswd verify command: cd /tmp && %s -vb %s %s [PASSWORD] 2>&1",
            WEB_CMD_EXEC_HTPASSWD, g_htpasswd_path, username);
    
    fp = popen(cmd_str, "r");
    if (NULL != fp) {
        memset(buffer, 0, sizeof(buffer));
        fgets(buffer, sizeof(buffer)-1, fp);
        
        if (strstr(buffer, "password verification failed")) {
            rt = VERITY_USER_RT_FAIL;
        } else if (strstr(buffer, "not found")) {
            rt = VERITY_USER_RT_NOT_FOUND;
        } else if (strstr(buffer, "correct")) {
            rt = VERITY_USER_RT_OK;
        }
        log_info("htpasswd verity User result : (%d)%s", rt, buffer);
        pclose(fp);
    }

    return rt;
}